package com.novel.utils;


import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.novel.exception.ArgumentException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;

import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class CommonUtil {

    private static String[] getNullPropertyNames(Object source) {
        BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = null;
            try {
                srcValue = src.getPropertyValue(pd.getName());
            } catch (Exception e) {
                log.error("转换异常:",e);
                continue;
            }
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }
        String[] result = new String[emptyNames.size()];
        src = null;
        pds = null;
        return emptyNames.toArray(result);
    }

    //随机字符
    public static String randomString(int len) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 基于spring的排除null参数的copy
     *
     * @param src    源
     * @param target 指向
     */
    public static void copyPropertiesIgnoreNull(Object src, Object target) {
        BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
    }

    /**
     * @param originalStr:原始通配字符
     * @param targetStr:         目标匹配字符
     */
    public static boolean checkStart(String originalStr, String targetStr) {
        if (StringUtils.isBlank(originalStr) || StringUtils.isBlank(targetStr)) {
            return false;
        }
        originalStr = originalStr.trim();
        targetStr = targetStr.trim();
        String originalStart = originalStr.split("\\*")[0];
        //如果目标字符串长度较小，返回false
        if (targetStr.length() < originalStart.length()) {
            return false;
        }
        String targetSubStr = targetStr.substring(0, originalStart.length());
        if (targetSubStr.equalsIgnoreCase(originalStart)) {
            return true;
        }
        return false;
    }

    /**
     * 缓存
     */
    private static final Map<String, BeanCopier> BEAN_COPIER_MAP = new HashMap<>();

    /**
     * 将source对象的属性拷贝到target对象中去
     * 基于cglib
     *
     * @param source source对象
     * @param target target对象
     */
    public static void copyProperties(Object source, Object target) {
        if (source != null && target != null) {
            String cacheKey = source.getClass().toString() + target.getClass().toString();
            BeanCopier beanCopier = null;
            if (!BEAN_COPIER_MAP.containsKey(cacheKey)) {
                synchronized (CommonUtil.class) {
                    if (!BEAN_COPIER_MAP.containsKey(cacheKey)) {
                        beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
                        BEAN_COPIER_MAP.put(cacheKey, beanCopier);
                    } else {
                        beanCopier = BEAN_COPIER_MAP.get(cacheKey);
                    }
                }
            } else {
                beanCopier = BEAN_COPIER_MAP.get(cacheKey);
            }
            beanCopier.copy(source, target, null);
        } else {
            throw new IllegalArgumentException("传入的类不能为null");
        }
    }

    /**
     * 参数校验
     *
     * @param bindingResult binding
     */
    public static void paramValid(BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            FieldError field = bindingResult.getFieldError();
            if (field != null) {
                throw new ArgumentException(String.format("%s", field.getDefaultMessage()));
            } else {
                throw new ArgumentException("参数有误！");
            }
        }
    }

    /**
     * string转列表
     *
     * @param strs 以，分割
     * @return list
     */
    public static List<String> stringToList(String strs) {
        if (strs == null || strs.trim().length() < 1) {
            return null;
        }
        String[] str = strs.split(",");
        return Arrays.asList(str);
    }

    /**
     * json序列化
     *
     * @param text   json
     * @param tClass class
     * @param <T>    bean
     * @return bean
     */
    public static <T> T convertClass(String text, Class<T> tClass) {
        T result = null;
        try {
            result = JSON.parseObject(text, tClass);
        } catch (Exception e) {
            log.error("转换异常:",e);
            throw new ArgumentException(String.format("json序列化失败 -%s", text));
        }
        return result;
    }

//    public static String getIpAddr(HttpServletRequest request) {
//        if (request == null) {
//            return "192.168.1.1";
//        }
//        String ip = request.getHeader("X-Forwarded-For");
//        System.out.println("X-Forwarded-For  ip=" + ip);
//        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
//            ip = request.getHeader("Proxy-Client-IP");
//            System.out.println("Proxy-Client-IP  ip=" + ip);
//        }
//        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
//            ip = request.getHeader("WL-Proxy-Client-IP");
//            System.out.println("WL-Proxy-Client-IP  ip=" + ip);
//        }
//        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
//            ip = request.getHeader("HTTP_CLIENT_IP");
//            System.out.println("HTTP_CLIENT_IP  ip=" + ip);
//        }
//        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
//            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
//            System.out.println("HTTP_X_FORWARDED_FOR  ip=" + ip);
//        }
//        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
//            ip = request.getRemoteAddr();
//            System.out.println("request.getRemoteAddr() ip=" + ip);
//        }
//        if (ip.contains(",")) {
//            String[] ips = ip.split(",");
//            ip = ips[0].trim();
//        }
//        return ip;
//    }

//    public static HttpServletRequest getHttpServletRequest() {
//        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
//        if (servletRequestAttributes == null) {
//            return null;
//        }
//        return servletRequestAttributes.getRequest();
//    }

//    public static String getCurrentIp() {
//        return getIpAddr(getHttpServletRequest());
//    }


    public static String getCurrentHost() {
        String host = "No found";
        try {
            InetAddress address = InetAddress.getLocalHost();
            host = address.getHostAddress();
        } catch (Exception e) {
            log.error("转换异常:",e);
        }
        return host;
    }

    /**
     * 时间转换
     *
     * @param time    时间
     * @param pattern 格式
     * @return date
     */
    public static Date parseDatetime(String time, String pattern) {
        if (null == time || time.length() == 0) {
            return new Date();
        }
        if (StringUtils.isBlank(pattern)) {
            pattern = "yyyyMMddHHmmss";
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        LocalDateTime localDateTime = LocalDateTime.parse(time, formatter);
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * 时间转换为local date time
     *
     * @param time    时间字符串
     * @param pattern 格式
     * @return ldt
     */
    public static LocalDateTime parseLocalDateTime(String time, String pattern) {
        if (null == time || time.length() == 0) {
            return LocalDateTime.now();
        }
        if (StringUtils.isBlank(pattern)) {
            pattern = "yyyyMMddHHmmss";
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.parse(time, formatter);
    }

    /**
     * 获取当前年月日
     *
     * @return str
     */
    public static String nowString() {
        return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }

    /**
     * 获取6天前
     */
    public static String preWeakString() {
        return LocalDate.now().plusDays(6).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }


//    public static boolean needDecrypt(MethodParameter parameter) {
//
//        boolean classPresent = parameter.getContainingClass().isAnnotationPresent(ValidRequest.class);
//
//        if (classPresent) {
//            return true;
//        } else if (parameter.getMethod() == null) {
//            return false;
//        }
//        return parameter.getMethod().isAnnotationPresent(ValidRequest.class);
//    }

    /**
     * 流读取json字符串
     *
     * @param inputStream 流
     * @return str
     */
    public static String json(InputStream inputStream) {
        if (inputStream == null) {
            return "";
        }
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            char[] charBuffer = new char[215];
            int bytesRead = -1;
            while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                stringBuilder.append(charBuffer, 0, bytesRead);
            }
        } catch (IOException ex) {
            log.error("转换异常:",ex);
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return stringBuilder.toString();
    }


    private static final ObjectMapper MAPPER = new ObjectMapper();

    public static <T> T parseJsonToBean(String content, Class<T> valueType) throws JsonProcessingException {
        return MAPPER.readValue(content, valueType);
    }

    public static <T> String parseBeanToJson(T t) throws JsonProcessingException {
        return MAPPER.writeValueAsString(t);
    }


    private static Pattern linePattern = Pattern.compile("_(\\w)");
    private static Pattern humpPattern = Pattern.compile("[A-Z]");

    /**
     * 下划线对象转换为驼峰对象
     *
     * @param source 下划线对象
     * @param target 驼峰对象
     * @return
     */
    public static void ulObj2HumpObj(Object source, Object target) {
        Map<String, Object> map1 = obj2Map(source);
        Map<String, Object> map2 = new HashMap<>(10);
        for (String key : map1.keySet()) {
            map2.put(lineToHump(key), map1.get(key));
        }
        Object obj = map2Obj(map2, target.getClass());
        BeanUtils.copyProperties(obj, target);
    }

    /**
     * 驼峰对象转换为下划线对象
     *
     * @param source 驼峰对象
     * @param target 下划线对象
     * @return
     */
    public static void humpObj2UlObj(Object source, Object target) {
        Map<String, Object> map1 = obj2Map(source);
        Map<String, Object> map2 = new HashMap<>(10);
        for (String key : map1.keySet()) {
            map2.put(humpToLine(key), map1.get(key));
        }
        Object obj = map2Obj(map2, target.getClass());
        BeanUtils.copyProperties(obj, target);
    }


    /**
     * 实体对象转成Map
     *
     * @param obj obj
     * @return map
     */
    public static Map<String, Object> obj2Map(Object obj) {
        Map<String, Object> map = new HashMap<>(10);
        if (obj == null) {
            return map;
        }
        @SuppressWarnings("rawtypes")
        Class clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(obj));
            }
        } catch (Exception e) {
            log.error("转换异常:",e);
        }
        return map;
    }

    /**
     * Map转成实体对象
     *
     * @param map   map实体对象包含属性
     * @param clazz 实体对象类型
     * @return
     */
    public static Object map2Obj(Map<String, Object> map, Class<?> clazz) {
        if (map == null) {
            return null;
        }
        Object obj = null;
        try {
            obj = clazz.newInstance();

            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                int mod = field.getModifiers();
                if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                    continue;
                }
                field.setAccessible(true);
                field.set(obj, map.get(field.getName()));
            }
        } catch (Exception e) {
            log.error("转换异常:",e);
            e.printStackTrace();
        }
        return obj;
    }


    /**
     * 驼峰转下划线,最后转为大写
     *
     * @param str
     * @return
     */
    public static String humpToLine(String str) {
        Matcher matcher = humpPattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString().toLowerCase();
    }

    /**
     * 下划线转驼峰,正常输出
     *
     * @param str
     * @return
     */
    public static String lineToHump(String str) {
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


    /**
     * 将对象的大写转换为下划线加小写，例如：userName-->user_name
     *
     * @param object
     * @return
     * @throws JsonProcessingException
     */
    public static String toUnderlineJSONString(Object object) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        String reqJson = mapper.writeValueAsString(object);
        return reqJson;
    }

    /**
     * 将下划线转换为驼峰的形式，例如：user_name-->userName
     *
     * @param json
     * @param clazz
     * @return
     * @throws IOException
     */
    public static <T> T toSnakeObject(String json, Class<T> clazz) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        T reqJson = mapper.readValue(json, clazz);
        return reqJson;
    }

}
